// 添加自动黑名单功能
module.exports = (options = {}) => {
  return async function rateLimit(ctx, next) {
    const { app } = ctx;
    const { redis } = app;
    
    // 获取客户端IP
    const clientIP = ctx.ip;
    // 获取请求路径
    const requestPath = ctx.path;
    // 获取用户ID（已登录用户）
    const userId = ctx.user?.user_id || 'anonymous';
    
    // 默认配置
    const defaultConfig = {
      windowMs: 60 * 1000,      // 时间窗口，默认60秒
      maxRequests: 60,          // 最大请求数，默认60次/分钟
      keyPrefix: 'ratelimit:',  // Redis键前缀
      statusCode: 429,          // 超限状态码
      message: '请求过于频繁，请稍后再试',
      whitelist: [],            // IP白名单
      blacklist: [],            // IP黑名单
      // 自动黑名单配置
      autoBlacklist: {
        enabled: true,          // 是否启用自动黑名单
        threshold: 3,           // 触发自动黑名单的超限次数
        duration: 3600,         // 黑名单持续时间(秒)，默认1小时
      },
      // 特定路径的配置
      pathRules: {
        // 例如: '/api/login': { windowMs: 60000, maxRequests: 5 }
      }
    };
    
    // 合并配置
    const config = { ...defaultConfig, ...options };
    
    // 黑名单Redis键
    const blacklistKey = `${config.keyPrefix}blacklist`;
    
    // 检查是否在黑名单中
    const isBlacklisted = await redis.sismember(blacklistKey, clientIP);
    if (isBlacklisted || config.blacklist.includes(clientIP)) {
      ctx.status = 403;
      ctx.body = { code: 403, message: '您的访问已被禁止' };
      return;
    }
    
    // 检查是否在白名单中
    if (config.whitelist.includes(clientIP)) {
      return await next();
    }
    
    // 获取当前路径的限制规则
    const pathConfig = config.pathRules[requestPath] || {};
    const windowMs = pathConfig.windowMs || config.windowMs;
    const maxRequests = pathConfig.maxRequests || config.maxRequests;
    
    // 构建Redis键
    // 使用用户ID和IP组合，兼顾已登录和未登录场景
    const key = `${config.keyPrefix}${userId}:${clientIP}:${requestPath}`;
    
    // 超限计数器Redis键
    const violationKey = `${config.keyPrefix}violations:${clientIP}`;
    
    try {
      // 使用Redis的MULTI和EXEC实现原子操作
      const multi = redis.multi();
      multi.incr(key);
      multi.pttl(key);  // 获取剩余过期时间
      
      const results = await multi.exec();
      const count = results[0][1];
      let ttl = results[1][1];
      
      // 如果是第一次请求，设置过期时间
      if (count === 1) {
        await redis.expire(key, Math.floor(windowMs / 1000));
        ttl = windowMs;
      }
      
      // 设置响应头，提供限制信息
      ctx.set('X-RateLimit-Limit', maxRequests.toString());
      ctx.set('X-RateLimit-Remaining', Math.max(0, maxRequests - count).toString());
      ctx.set('X-RateLimit-Reset', Math.ceil(ttl / 1000).toString());
      
      // 判断是否超出限制
      if (count > maxRequests) {
        ctx.status = config.statusCode;
        ctx.body = {
          code: config.statusCode,
          message: pathConfig.message || config.message,
          retryAfter: Math.ceil(ttl / 1000)
        };
        ctx.set('Retry-After', Math.ceil(ttl / 1000).toString());
        
        // 记录超限日志
        app.logger.warn(`[RateLimit] IP: ${clientIP}, Path: ${requestPath}, UserId: ${userId} 请求频率超限`);
        
        // 处理自动黑名单逻辑
        if (config.autoBlacklist.enabled) {
          // 增加违规计数
          const violations = await redis.incr(violationKey);
          // 设置违规计数过期时间
          if (violations === 1) {
            await redis.expire(violationKey, 3600); // 1小时内的违规次数
          }
          
          // 如果违规次数超过阈值，加入黑名单
          if (violations >= config.autoBlacklist.threshold) {
            await redis.sadd(blacklistKey, clientIP);
            await redis.expire(blacklistKey, config.autoBlacklist.duration);
            
            app.logger.warn(`[RateLimit] IP: ${clientIP} 已被自动加入黑名单，持续时间: ${config.autoBlacklist.duration}秒`);
            
            // 更新响应消息
            ctx.body.message = '您的请求过于频繁，已被暂时禁止访问';
          }
        }
        
        return;
      }
      
      // 继续处理请求
      await next();
      
    } catch (error) {
      // 发生错误时记录日志
      app.logger.error(`[RateLimit] Error: ${error.message}`, error);
      // 出错时不阻止请求，保证系统可用性
      await next();
    }
  };
};